package labox.innovation.gameserver.model;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javolution.util.FastList;
import labox.innovation.config.Config;
import labox.innovation.gameserver.GameTimeController;
import labox.innovation.gameserver.model.actor.L2Character;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.model.actor.instance.L2SummonInstance;
import labox.innovation.gameserver.network.SystemMessageId;
import labox.innovation.gameserver.network.serverpackets.AbnormalStatusUpdate; //import labox.innovation.gameserver.network.serverpackets.ExOlympiadSpelledInfo;
//import labox.innovation.gameserver.network.serverpackets.MagicSkillLaunched;
//import labox.innovation.gameserver.network.serverpackets.MagicSkillUse;
import labox.innovation.gameserver.network.serverpackets.EffectAction;
import labox.innovation.gameserver.network.serverpackets.PartySpelled;
import labox.innovation.gameserver.network.serverpackets.SystemMessage;
import labox.innovation.gameserver.skills.AbnormalEffect;
import labox.innovation.gameserver.skills.Env;
import labox.innovation.gameserver.skills.Stats;
import labox.innovation.gameserver.skills.funcs.Func;
import labox.innovation.gameserver.skills.funcs.FuncTemplate;
import labox.innovation.gameserver.skills.funcs.Lambda;
import labox.innovation.gameserver.templates.effects.EffectTemplate;
import labox.innovation.gameserver.templates.skills.L2EffectType;
import labox.innovation.gameserver.timer.Timer;

/**
 *  技能效果 基类
 * @author zjj
 *
 */
public abstract class L2Effect {

	static final Logger _log = LoggerFactory.getLogger(L2Effect.class.getName());

	//效果的状态
	//创建 - 进行中 - 结束
	public static enum EffectState {
		CREATED, ACTING, FINISHING
	}

	private static final Func[] _emptyFunctionSet = new Func[0];

	//member _effector is the instance of L2Character that cast/used the spell/skill that is
	//causing this effect.  Do not confuse with the instance of L2Character that
	//is being affected by this effect.
	//释放者
	private final L2Character _effector;

	//member _effected is the instance of L2Character that was affected
	//by this effect.  Do not confuse with the instance of L2Character that
	//casted/used this effect.
	//受影响者
	private final L2Character _effected;

	//the skill that was used.
	//使用的技能
	private final L2Skill _skill;

	//or the items that was used.
	//private final L2Item _item;

	// the value of an update
	protected final Lambda _lambda;

	// the current state
	private EffectState _state;

	// period, seconds
	//
	private final int _period;

	/**
	 * 每次效果开始的ticks
	 * 这个值 需要保存到数据库中， 如果效果是 会持久存在的话.
	 */
	private int _periodStartTicks;

	private int _periodfirsttime;

	private EffectTemplate _template;

	// function templates
	// 效果附加影响的属性 
	private final FuncTemplate[] _funcTemplates;

	//initial count
	private int _totalCount;

	// counter
	//效果持续的轮数
	private int _count;

	// abnormal effect mask
	private AbnormalEffect _abnormalEffect;

	// special effect mask
	private AbnormalEffect _specialEffect;

	// show icon
	private int _icon;

	public boolean preventExitUpdate;

	public final class EffectTask implements Runnable {
		protected final int _delay;

		protected final int _rate;

		int _count = 0;

		EffectTask(int pDelay, int pRate) {
			_delay = pDelay;
			_rate = pRate;
		}

		public void run() {
			try {
				if (getPeriodfirsttime() == 0)
					setPeriodStartTicks(GameTimeController.getGameTicks());
				else
					setPeriodfirsttime(0);
				L2Effect.this.scheduleEffect();
			} catch (Exception e) {
				_log.error("EffectTask error",Config.SERVER_ID, e);
			}
		}
	}

	//	private ScheduledFuture<?> _currentFuture;
	private Timer _currentFuture;

	private EffectTask _currentTask;

	/** The Identifier of the stack group */
	private final String _stackType;

	/** The position of the effect in the stack group */
	private final float _stackOrder;

	//效果是否已使用
	private boolean _inUse = false;

	private boolean _startConditionsCorrect = true;

	/**
	 * For special behavior. See Formulas.calcEffectSuccess
	 */
	private double _effectPower; //影响效果的成功几率. 如果为 -1 那么表示 100%成功

	private L2EffectType _effectSkillType;

	/**
	 * <font color="FF0000"><b>WARNING: scheduleEffect nolonger inside constructor</b></font><br>
	 * So you must call it explicitly
	 */
	protected L2Effect(Env env, EffectTemplate template) {
		_state = EffectState.CREATED;
		_skill = env.skill;
		//_item = env._item == null ? null : env._item.getItem();
		_template = template;
		_effected = env.target;
		_effector = env.player;
		_lambda = template.lambda;
		_funcTemplates = template.funcTemplates;
		_count = template.counter;
		_totalCount = _count;

		// Support for retail herbs duration when _effected has a Summon 
		int temp = template.period;

		if ((_skill.getId() > 2277 && _skill.getId() < 2286) || (_skill.getId() >= 2512 && _skill.getId() <= 2514)) {
			if (_effected instanceof L2SummonInstance || (_effected instanceof FuzePcInstance && ((FuzePcInstance) _effected).getPet() instanceof L2SummonInstance)) {
				temp /= 2;
			}
		}
		
		if( _effector != null )
		{
			temp = (int) _effector.calcStat(Stats.STUNTIME, temp, null, _skill);
		}

		_period = temp;
		_abnormalEffect = template.abnormalEffect;
		_specialEffect = template.specialEffect;
		_stackType = template.stackType;
		_stackOrder = template.stackOrder;
		_periodStartTicks = GameTimeController.getGameTicks();
		_periodfirsttime = 0;
		_icon = template.effectIconId;
		_effectPower = template.effectPower;
		_effectSkillType = template.effectType;

		/*
		 * Commented out by DrHouse: 
		 * scheduleEffect can call onStart before effect is completly
		 * initialized on constructor (child classes constructor)
		 */
		//scheduleEffect();
	}

	/**
	 * Special constructor to "steal" buffs. Must be implemented on
	 * every child class that can be stolen.<br><br>
	 * 
	 * <font color="FF0000"><b>WARNING: scheduleEffect nolonger inside constructor</b></font>
	 * <br>So you must call it explicitly
	 * @param env
	 * @param effect
	 */
	protected L2Effect(Env env, L2Effect effect) {
		_template = effect._template;
		_state = EffectState.CREATED;
		_skill = env.skill;
		_effected = env.target;
		_effector = env.player;
		_lambda = _template.lambda;
		_funcTemplates = _template.funcTemplates;
		_count = effect.getCount();
		_totalCount = _template.counter;
		_period = _template.period - effect.getTime();
		_abnormalEffect = _template.abnormalEffect;
		_specialEffect = _template.specialEffect;
		_stackType = _template.stackType;
		_stackOrder = _template.stackOrder;
		_periodStartTicks = effect.getPeriodStartTicks();
		_periodfirsttime = effect.getPeriodfirsttime();
		_icon = _template.effectIconId;

		/*
		 * Commented out by DrHouse: 
		 * scheduleEffect can call onStart before effect is completly
		 * initialized on constructor (child classes constructor)
		 */
		//scheduleEffect();
	}

	public int getCount() {
		return _count;
	}

	public int getTotalCount() {
		return _totalCount;
	}

	/**
	 * 当 角色 登陆游戏 时， 获的未消失的效果 
	 * 使用以下2函数
	 * setCount 效果作用次数
	 * @param newcount
	 */
	public void setCount(int newcount) {
//		System.out.println("数据库恢复效果  效果作用时间:" + newcount);
		_count = newcount;
	}

	/**
	 *  效果 从数据库还原时 调用的接口
	 *  
	 * @param newfirsttime : 距离下一次效果开始的 时间差
	 */
	public void setFirstTime(int newfirsttime) {

//		System.out.println("数据库恢复效果  效果时间:" + newfirsttime);

		if (_currentFuture != null) {

			_periodStartTicks = GameTimeController.getGameTicks() - newfirsttime * GameTimeController.TICKS_PER_SECOND;
			_currentFuture.cancel();
			_currentFuture = null;
			_currentTask = null;
			_periodfirsttime = newfirsttime;
			int duration = (_period / 1000) - _periodfirsttime;
//			System.out.println("持续时间 :" + duration);
			//_log.warning("Period: "+_period+"-"+_periodfirsttime+"="+duration);
			_currentTask = new EffectTask(duration * 1000, -1);
			_currentFuture = new Timer(duration * 1000, false, _currentTask);
			_currentFuture.start();

			//TODO:
			/**
			 * 上面可以不 new新的定时器 而是 设置它的 delay时间。
			 * _currentFuture.setDelay( duration * 1000 );
			 */

			//			_currentFuture = ThreadPoolManager.getInstance().scheduleEffect(_currentTask, duration * 1000);
		}
	}

	public boolean getShowIcon() {
		return _icon != -1;
	}

	public int getPeriod() {
		return _period;
	}

	/**
	 *  获得这个效果 已作用了的时间 (秒)
	 * @return
	 */
	public int getTime() {
		return (GameTimeController.getGameTicks() - _periodStartTicks) / GameTimeController.TICKS_PER_SECOND;
	}

	/**
	 * 获得状态剩余时间
	 * @return
	 */
	public int getRemainTime() {
		return (int) ((float) (_period / 1000) - getTime());
	}

	/**
	 * Returns the elapsed time of the task.
	 * @return Time in seconds.
	 */
	public int getTaskTime() {
		if (_count == _totalCount)
			return 0;
		float temp = (float) _period / 1000;
		return (int) ((Math.abs(_count - _totalCount + 1) * temp) + getTime() + 1);
	}

	public boolean getInUse() {
		return _inUse;
	}

	public boolean setInUse(boolean inUse) {
		_inUse = inUse;
		if (_inUse)
			_startConditionsCorrect = onStart();
		else
			onExit();

		return _startConditionsCorrect;
	}

	public String getStackType() {
		return _stackType;
	}

	public float getStackOrder() {
		return _stackOrder;
	}

	public final L2Skill getSkill() {
		return _skill;
	}

	public final L2Character getEffector() {
		return _effector;
	}

	public final L2Character getEffected() {
		return _effected;
	}

	public boolean isSelfEffect() {
		return _skill._effectTemplatesSelf != null;
	}

	//是否是 药物 效果
	public boolean isHerbEffect() {
		if (getSkill().getName().contains("Herb"))
			return true;

		return false;
	}

	/**
	 * 计算效果影响的数值
	 * @return
	 */
	public final double calc() {
		Env env = new Env();
		env.player = _effector;
		env.target = _effected;
		env.skill = _skill;
		return _lambda.calc(env);
	}

	//
	private synchronized void startEffectTask(int duration) {
		if (duration >= 0) {
			stopEffectTask();
			_currentTask = new EffectTask(duration, -1);
			_currentFuture = new Timer(duration, false, _currentTask);
			_currentFuture.start();
			//			_currentTask = new EffectTask(duration, -1);
			//			_currentFuture = ThreadPoolManager.getInstance().scheduleEffect(_currentTask, duration);
		}
		//		效果创建成功，加入到拥有者的效果链中去
		if (_state == EffectState.ACTING) {
			_effected.addEffect(this);
		}
	}

	//循环效果?
	//持续效果？
	private synchronized void startEffectTaskAtFixedRate(int delay, int rate) {
		stopEffectTask();

		_currentTask = new EffectTask(delay, rate);
		_currentFuture = new Timer(rate, true, _currentTask);
		_currentFuture.start();
		//		_currentTask = new EffectTask(delay, rate);
		//		_currentFuture = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(_currentTask, delay, rate);
		//效果创建成功，加入到拥有者的效果链中去
		if (_state == EffectState.ACTING) {
			_effected.addEffect(this);
		}
	}

	/**
	 * Stop the L2Effect task and send Server->Client update packet.<BR><BR>
	 *
	 * <B><U> Actions</U> :</B><BR><BR>
	 * <li>Cancel the effect in the the abnormal effect map of the L2Character </li>
	 * <li>Stop the task of the L2Effect, remove it and update client magic icon </li><BR><BR>
	 *
	 */
	public final void exit() {
		this.exit(false);
	}

	public final void exit(boolean preventUpdate) {
		preventExitUpdate = preventUpdate;
		_state = EffectState.FINISHING;
		scheduleEffect();
	}

	/**
	 * Stop the task of the L2Effect, remove it and update client magic icon.<BR><BR>
	 *
	 * <B><U> Actions</U> :</B><BR><BR>
	 * <li>Cancel the task </li>
	 * <li>Stop and remove L2Effect from L2Character and update client magic icon </li><BR><BR>
	 *
	 */
	//取消任务， 角色效果结束， 并从角色中移除掉这个效果
	public void stopEffectTask() {
		if (_currentFuture != null) {
			// Cancel the task
			_currentFuture.cancel();
			_currentFuture = null;
			_currentTask = null;

			if (getEffected() != null) {
				getEffected().removeEffect(this);
			}
		}
	}

	/** returns effect type */
	//在各个子类中实现
	//每个子类 都有不同的效果类型
	public abstract L2EffectType getEffectType();

	/** Notify started */
	//通知角色，开始效果
	public boolean onStart() {
//		System.out.println("效果开始:" + this.getClass().getName());
		if (_skill.getEffectCartoonId() != -1) {
			//通知效果动画开始
			EffectAction ea = new EffectAction(getEffected().getObjectId(), _skill.getEffectCartoonId(), _skill.getEffectIdType(), EffectAction.OP_TYPE_START);
			getEffected().broadcastPacket(ea);
		}
		if (_abnormalEffect != AbnormalEffect.NULL)
			getEffected().startAbnormalEffect(_abnormalEffect);
		if (_specialEffect != AbnormalEffect.NULL)
			getEffected().startSpecialEffect(_specialEffect);
		return true;
	}
	
	/**
	 * 是否有持续效果动画
	 * @return
	 */
	public boolean hasContinuseEffectCartoon(){
		return ( _inUse && _skill.getEffectCartoonId() != -1 && _skill.getEffectIdType() == 1 );
	}
	
	/**
	 * 获得 效果动画id
	 * @return
	 */
	public int getEffectCartoonId(){
		
		return _skill.getEffectCartoonId();
		
	}
	
	/**
	 * 获得效果动画类型
	 * @return
	 */
	public int getEffectIdType(){
		
		return _skill.getEffectIdType();
	}

	/**
	 * Cancel the effect in the the abnormal effect map of the effected L2Character.<BR><BR>
	 */
	//通知 角色效果结束 或 停止
	public void onExit() {
		//		Thread.dumpStack();
		//		System.out.println("效果退出:" + this.getClass().getName() );
		if (_skill.getEffectCartoonId() != -1) {
			//通知效果动画结束
			EffectAction ea = new EffectAction(getEffected().getObjectId(), _skill.getEffectCartoonId(), _skill.getEffectIdType(), EffectAction.OP_TYPE_END);
			getEffected().broadcastPacket(ea);
		}

		if (_abnormalEffect != AbnormalEffect.NULL)
			getEffected().stopAbnormalEffect(_abnormalEffect);
		if (_specialEffect != AbnormalEffect.NULL)
			getEffected().stopSpecialEffect(_specialEffect);
	}

	/** Return true for continuation of this effect */
	//效果的持续时间
	public abstract boolean onActionTime();

	//效果重新开始
	public final void rescheduleEffect() {
		if (_state != EffectState.ACTING) {
			scheduleEffect();
		} else {
			if (_count > 1) {
				//				startEffectTaskAtFixedRate(5, _period * 1000);
				startEffectTaskAtFixedRate(5, _period);
				return;
			}
			if (_period > 0) {
				//				startEffectTask(_period * 1000);
				startEffectTask(_period);
				return;
			}
		}
	}

	public final void scheduleEffect() {
		if (_state == EffectState.CREATED) {
			_state = EffectState.ACTING;

			//不在发送系统提示给客户端
			if (_skill.isPvpSkill() && getShowIcon() && getEffected() instanceof FuzePcInstance) {
				SystemMessage smsg = new SystemMessage(SystemMessageId.YOU_FEEL_S1_EFFECT);
				smsg.addSkillName(_skill);
				getEffected().sendPacket(smsg);
			}

			//count效果持续的轮数 不为0的话
			//每period秒一次
			if (_count > 1) {
				//				startEffectTaskAtFixedRate(5, _period * 1000);
				startEffectTaskAtFixedRate(5, _period);
				return;
			}

			//如果效果持续轮数为 0 
			if (_period > 0 || _period == -1) {
				//				startEffectTask(_period * 1000);
				startEffectTask(_period);
				return;
			}
			// effects not having count or period should start
			_startConditionsCorrect = onStart();
		}

		if (_state == EffectState.ACTING) {
			if (_count-- > 0) {
				if (getInUse()) { // effect has to be in use
					if (onActionTime() && _startConditionsCorrect)
						return; // false causes effect to finish right away
				} else if (_count > 0) { // do not finish it yet, in case reactivated
					return;
				}
			}
			_state = EffectState.FINISHING;
		}

		if (_state == EffectState.FINISHING) {
			// Cancel the effect in the the abnormal effect map of the L2Character
			//效果正常结束
			if (getInUse() || !(_count > 1 || _period > 0))
				if (_startConditionsCorrect)
					onExit();

			//If the time left is equal to zero, send the message
			if (_count == 0 && getShowIcon() && getEffected() instanceof FuzePcInstance) {
				SystemMessage smsg3 = new SystemMessage(SystemMessageId.S1_DISAPPEARED);
				smsg3.addSkillName(_skill);
				getEffected().sendPacket(smsg3);
			}

			// if task is null - stopEffectTask does not remove effect
			if (_currentFuture == null && getEffected() != null) {
				getEffected().removeEffect(this);
			}

			// Stop the task of the L2Effect, remove it and update client magic icon
			stopEffectTask();

		}

	}// end scheduleEffect();

	//状态计算函数
	public Func[] getStatFuncs() {
		if (_funcTemplates == null)
			return _emptyFunctionSet;
		List<Func> funcs = new FastList<Func>();
		for (FuncTemplate t : _funcTemplates) {
			Env env = new Env();
			env.player = getEffector();
			env.target = getEffected();
			env.skill = getSkill();
			Func f = t.getFunc(env, this); // effect is owner
			if (f != null)
				funcs.add(f);
		}
		if (funcs.isEmpty())
			return _emptyFunctionSet;
		return funcs.toArray(new Func[funcs.size()]);

	}

	//
	public final void addIcon(AbnormalStatusUpdate mi) {
		EffectTask task = _currentTask;
		//		ScheduledFuture<?> future = _currentFuture;
		Timer future = _currentFuture;
		if (_state == EffectState.FINISHING || _state == EffectState.CREATED)
			return;
		L2Skill sk = getSkill();
		if (task != null && task._rate > 0) {
			//			if (sk.isPotion())
			//				mi.addEffect(sk.getId(), getLevel(), sk.getBuffDuration() - (getTaskTime() * 1000));
			//			else
			mi.addEffect(sk.getId(), getLevel(), -1);
		} else if (future != null)
			mi.addEffect(sk.getId(), getLevel(), (int) future.getDelay() * 100);
		else if (_period == -1)
			mi.addEffect(sk.getId(), getLevel(), _period);

	}

	public final void addPartySpelledIcon(PartySpelled ps) {
		//		ScheduledFuture<?> future = _currentFuture;
		Timer future = _currentFuture;
		if (_state == EffectState.FINISHING || _state == EffectState.CREATED)
			return;
		L2Skill sk = getSkill();
		if (future != null)
			ps.addPartySpelledEffect(sk.getId(), getLevel(), (int) future.getDelay() * 100);
		else if (_period == -1)
			ps.addPartySpelledEffect(sk.getId(), getLevel(), _period);
	}

	public int getLevel() {
		return getSkill().getLevel();
	}

	public int getPeriodfirsttime() {
		return _periodfirsttime;
	}

	public void setPeriodfirsttime(int periodfirsttime) {
		_periodfirsttime = periodfirsttime;
	}

	public int getPeriodStartTicks() {
		return _periodStartTicks;
	}

	/**
	 * 设置 每次效果开始的 ticks
	 * @param periodStartTicks
	 */
	public void setPeriodStartTicks(int periodStartTicks) {
		_periodStartTicks = periodStartTicks;
	}

	/**
	 * 获得效果id
	 * @return
	 */
	public int getEffectIconId() {
		return _icon;
	}

	public EffectTemplate getEffectTemplate() {
		return _template;
	}

	public double getEffectPower() {
		return _effectPower;
	}

	public L2EffectType getSkillType() {
		return _effectSkillType;
	}
}